package mips.instructions.cop0;

import mips.instructions.Instruction;

/**
 * <code>ERET</code> instruction<br/>
 * Exception Return<br/>
 * @author jnmartin84@gmail.com
 */
public class ERET extends Instruction {

	private static final ERET INSTANCE = new ERET();
	private static final String INSTRUCTION_NAME = "ERET";

	private ERET(){}

	public static final ERET getInstance() {
		return INSTANCE;
	}

	/**
	 * <b>Format:</b><br/>
	 * ERET<br/><br/>
	 * <b>Description:</b><br/>
	 * ERET is the R4000 instruction for returning from an interrupt, exception,<br/>
	 * or error trap. Unlike a branch or jump instruction, ERET does not execute<br/>
	 * the next instruction.<br/><br/>
	 * ERET must not itself be placed in a branch delay slot.<br/><br/>
	 * If the processor is servicing an error trap (SR<sub>2</sub> = 1), then load the PC from<br/>
	 * the ErrorEPC and clear the ERL bit of the Status register (SR<sub>2</sub>). Otherwise<br/>
	 * (SR<sub>2</sub> = 0), load the PC from the EPC, and clear the EXL bit of the Status<br/>
	 * register (SR<sub>1</sub>).<br/><br/>
	 * An ERET executed between a LL and SC also causes the SC to fail.<br/><br/>
	 * <b>Operation:</b><br/>
	 * T: if SR<sub>2</sub> = 1 then<br/>
	 * PC &larr; ErrorEPC<br/>
	 * SR &larr; SR<sub>31&hellip;3</sub> || 0 || SR<sub>1&hellip;0</sub><br/>
	 * else<br/>
	 * PC &larr; EPC<br/>
	 * SR &larr; SR<sub>31&hellip;2</sub> || 0 || SR<sub>0</sub><br/>
	 * endif</br>
	 * LLbit &larr; 0
	 */
	@Override
	public final void execute(final int instruction) {

		// SR is 12, bit to check is 2
		if((mips.COP0.CONTROL[12] & 0x00000004) != 0) {

			// ErrorEPC is 30
			mips.R4300i.PC = (int) (mips.COP0.CONTROL[30] & 0x00000000FFFFFFFFL);
			mips.R4300i.nPC = mips.R4300i.PC + 4;
			// set ERL (bit 2) to 0
			mips.COP0.CONTROL[12] &= 0xFFFFFFFB;
		}
		else {

			// EPC is 14
			mips.R4300i.PC = (int) (mips.COP0.CONTROL[14] & 0x00000000FFFFFFFFL);
			mips.R4300i.nPC = mips.R4300i.PC + 4;
			// set EXL (bit 1) to 0
			mips.COP0.CONTROL[12] &= 0xFFFFFFFD;
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String emit(final int instruction) {
		return	"		if((mips.COP0.REG[12] & 0x00000004) != 0) {\n" + 
				"			\n" + 
				"			mips.CPU.PC = (int) (mips.COP0.CONTROL[30] & 0x00000000FFFFFFFFL);\n" + 
				"			mips.CPU.nPC = mips.CPU.PC + 4;\n" + 
				"			mips.COP0.REG[12] &= 0xFFFFFFFB;\n" + 
				"		}\n" + 
				"		else {\n" + 
				"			\n" + 
				"			mips.CPU.PC = (int) (mips.COP0.CONTROL[14] & 0x00000000FFFFFFFFL);\n" + 
				"			mips.CPU.nPC = mips.CPU.PC + 4;\n" + 
				"			mips.COP0.REG[12] &= 0xFFFFFFFD;\n" + 
				"		}\n";
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName(final int instruction) {
		return getName();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName() {
		return INSTRUCTION_NAME;
	}
}